#include "common_opr_all.h"

#include <openssl/hmac.h>
#include <openssl/md5.h>
#include <openssl/sha.h>
#include <openssl/crypto.h>
#include <openssl/x509.h>
#include <openssl/bio.h>
#include <openssl/objects.h>
#include <openssl/evp.h>
#include <openssl/pem.h>
#include <openssl/err.h>

CommonOprOpenssl::CommonOprOpenssl()
{
}

CommonOprOpenssl::~CommonOprOpenssl()
{
}

int CommonOprOpenssl::GetFile_Md5_Hex(const string FilePath, string &MD5_Hex_Out)
{

    FILE *pvFd = NULL;
    MD5_CTX mdContext;
    uint8_t *pvData = NULL;
    uint32_t vLen = 0;

    pvFd = fopen(FilePath.c_str(), "r");
    if (NULL == pvFd)
    {
        return -1;
    }

    pvData = (uint8_t *)calloc(1, 100 * 1024);
    if (pvData == NULL)
    {
        fclose(pvFd);
        return -1;
    }
    MD5_Init(&mdContext);
    while (0 != (vLen = fread(pvData, 1, 100 * 1024, pvFd)))
    {
        MD5_Update(&mdContext, pvData, vLen);
    }
    uint8_t vMD5_result[MD5_DIGEST_LENGTH] = {0};
    MD5_Final((unsigned char *)vMD5_result, &mdContext);

    fclose(pvFd);
    free(pvData);
    pvData = NULL;

    // 转16进制字符串
    CommonOprString::bytesToHexStr(vMD5_result, sizeof(vMD5_result), MD5_Hex_Out);
    Log_Mqtt("[CommonOprOpenssl] [GetFileMd5_Hex] MD5_Hex_Out: {}", MD5_Hex_Out);
    return 0;
}

int CommonOprOpenssl::GetFile_Md5_Hex_Lower(const string FilePath, string &MD5_Hex_Out)
{
    FILE *pvFd = NULL;
    MD5_CTX mdContext;
    uint8_t *pvData = NULL;
    uint32_t vLen = 0;

    pvFd = fopen(FilePath.c_str(), "r");
    if (NULL == pvFd)
    {
        return -1;
    }

    pvData = (uint8_t *)calloc(1, 100 * 1024);
    if (pvData == NULL)
    {
        fclose(pvFd);
        return -1;
    }
    MD5_Init(&mdContext);
    while (0 != (vLen = fread(pvData, 1, 100 * 1024, pvFd)))
    {
        MD5_Update(&mdContext, pvData, vLen);
    }
    uint8_t vMD5_result[MD5_DIGEST_LENGTH] = {0};
    MD5_Final((unsigned char *)vMD5_result, &mdContext);

    fclose(pvFd);
    free(pvData);
    pvData = NULL;

    // 转16进制字符串
    CommonOprString::bytesToHexStrLower(vMD5_result, sizeof(vMD5_result), MD5_Hex_Out);
    Log_Mqtt("[CommonOprOpenssl] [GetFileMd5_Hex] MD5_Hex_Out: {}", MD5_Hex_Out);
    return 0;
}

int CommonOprOpenssl::GetFile_MD5x509_Hex(const string FilePath, string &MD5_x509Hex_Out)
{

    // 从文件中读取证书
    BIO *certBio = BIO_new_file(FilePath.c_str(), "r");
    if (!certBio)
    {
        Log_ERROR("[CommonOprOpenssl]  open file failed.");
        return -1;
    }
    X509 *cert = PEM_read_bio_X509(certBio, nullptr, nullptr, nullptr);
    if (!cert)
    {
        Log_ERROR("[CommonOprOpenssl]  read file failed.");
        return -1;
    }
    BIO_free(certBio);

    //获取公钥证书的二进制编码
    BIO *bio = BIO_new(BIO_s_mem());
    i2d_X509_bio(bio, cert);
    BUF_MEM *bufferPtr;
    BIO_get_mem_ptr(bio, &bufferPtr);
    std::string certificateBinary((char *)bufferPtr->data, bufferPtr->length);

    BIO_free_all(bio);
    X509_free(cert);

    uint8_t vMD5_result[MD5_DIGEST_LENGTH] = {0};
    MD5(reinterpret_cast<const unsigned char *>(certificateBinary.c_str()), certificateBinary.size(), vMD5_result);

    // Log_Mqtt("[CommonOprOpenssl] [GetFile_MD5x509_Hex] call bytesToHexStr");

    // 转16进制字符串
    CommonOprString::bytesToHexStr(vMD5_result, sizeof(vMD5_result), MD5_x509Hex_Out);
    // Log_Mqtt("[CommonOprOpenssl] [GetFile_MD5x509_Hex] MD5_x509Hex_Out: {}", MD5_x509Hex_Out);
    return 0;
}

string CommonOprOpenssl::GetCertCharVal_MD5x509_Hex(const string certaddstr)
{
    int32_t vRet = 0;
    (void)vRet;

    // 以X509的格式读取并计算计算密钥证书的MD5值
    string filepath = global_data::g_http_ca_path + global_data::g_client_cert;
    // Log_Mqtt("[CommonOprOpenssl] filepath: {}", filepath);

    string MD5_x509Hex_str = "";
    GetFile_MD5x509_Hex(filepath.c_str(), MD5_x509Hex_str);

    string certcharfullstr = MD5_x509Hex_str + certaddstr;
    string Sha256HexStr = GetStringSha256HexStr(certcharfullstr);
    // Log_Mqtt("[CommonOprOpenssl] [GetCertCharVal_MD5x509_Hex] certcharfullstr, Sha256HexStr: {}", Sha256HexStr);
    return Sha256HexStr;
}

int CommonOprOpenssl::HmacEncode_Hex(const string algo, const string key, const string strIn, string &strOut)
{
    const EVP_MD *engine = NULL;
    if ("sha512" == algo)
    {
        engine = EVP_sha512();
    }
    else if ("sha256" == algo)
    {
        engine = EVP_sha256();
    }
    else if ("shamd5" == algo)
    {
        engine = EVP_md5();
    }
    else if ("sha384" == algo)
    {
        engine = EVP_sha384();
    }
    else
    {
        Log_Info("[CommonOprOpenssl] [HmacEncode] Algorithm: {}  is not supported by this program!", algo);
        return -1;
    }

    unsigned char *output = (unsigned char *)malloc(EVP_MAX_MD_SIZE);
    if (output == nullptr)
    {
        return -1;
    }

    memset(output, 0, EVP_MAX_MD_SIZE);
    unsigned int output_length;

    HMAC_CTX *pctx = NULL;
    pctx = HMAC_CTX_new();
    if (pctx == NULL)
    {
        free(output);
        return -1;
    }
    HMAC_Init_ex(pctx, key.c_str(), key.size(), engine, NULL);
    HMAC_Update(pctx, (unsigned char *)strIn.c_str(), strIn.size());

    HMAC_Final(pctx, output, &output_length);
    HMAC_CTX_free(pctx);

    // 转16进制字符串
    CommonOprString::bytesToHexStr(output, output_length, strOut);
    free(output);
    return 0;
}

string CommonOprOpenssl::GetStringSha256HexStr(const string targetstr)
{

    uint8_t Sha256str[32] = {0};
    string Sha256HexStrOut;

    // Log_Mqtt("[CommonOprOpenssl] [GetStringSha256HexStr] targetstr.size: {}", targetstr.size());
    // Log_Mqtt("[CommonOprOpenssl] [GetStringSha256HexStr] targetstr.c_str:\n{}", targetstr.c_str());

    SHA256((unsigned char *)targetstr.c_str(), targetstr.size(), Sha256str);

    // 转16进制字符串
    CommonOprString::bytesToHexStr(Sha256str, sizeof(Sha256str), Sha256HexStrOut);
    // Log_Mqtt("[CommonOprOpenssl] [GetStringSha256HexStr] Sha256HexStrOut:\n {}", Sha256HexStrOut);

    return Sha256HexStrOut;
}

uint8_t CommonOprOpenssl::BCC(const uint8_t *data, size_t dataLength)
{
    uint8_t check = 0;
    int i = 0;
    while (dataLength--)
    {
        try
        {
            check ^= data[i++];
        }
        catch (const std::exception &e)
        {
            std::cerr << e.what() << '\n';
        }
    }
    return check;
}

string CommonOprOpenssl::encodeFromArray(const uint8_t *data, size_t len)
{
    size_t i;
    char c;
    std::string ret;

    std::string Base64Table = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
    char fillchar = '=';

    ret.reserve(len * 2);

    for (i = 0; i < len; ++i)
    {
        c = (data[i] >> 2) & 0x3f;
        ret.append(1, Base64Table[c]);
        c = (data[i] << 4) & 0x3f;
        if (++i < len)
        {
            c |= (data[i] >> 4) & 0x0f;
        }

        ret.append(1, Base64Table[c]);
        if (i < len)
        {
            c = (data[i] << 2) & 0x3f;
            if (++i < len)
            {
                c |= (data[i] >> 6) & 0x03;
            }
            ret.append(1, Base64Table[c]);
        }
        else
        {
            ++i;
            ret.append(1, fillchar);
        }

        if (i < len)
        {
            c = data[i] & 0x3f;
            ret.append(1, Base64Table[c]);
        }
        else
        {
            ret.append(1, fillchar);
        }
    }

    return (ret);
}
